import {Injectable} from '@nestjs/common';
import * as fs from "fs";
import * as Path from "path";
import {root} from "../../config";
import {TagEntity} from "./entity/tag.entity";
import {ValidateException} from "../../excepiton/ValidateException";
import {TagFileEntity} from "./entity/tagFile.entity";
import {FileEntity} from "../file/file.entity";
import {FileService} from "../file/file.service";
import {pathJoin} from "../../util/common";

const TagsPath = pathJoin(root, `../data/tags.json`);
const TagFilesPath = pathJoin(root, `../data/tagFiles.json`);

@Injectable()
export class TagService {

    private uniqueValidate = new UniqueValidate('value');

    async fetch(keyWords: string): Promise<Array<TagEntity>> {
        let json = TagService.getTags();
        if (keyWords) {
            json = json.filter(j => j.value.includes(keyWords))
        }
        return json;
    }

    public static renameFilePath(oldPath: string, newPath: string) {
        const tagFiles = TagService.getTagFiles();
        const findIndexes = [];
        tagFiles.filter((value, index) => {
            const b = value.filePath.startsWith(oldPath);
            if (b) {
                findIndexes.push(index)
            }
        })
        for (let findIndex of findIndexes) {
            tagFiles[findIndex].filePath = tagFiles[findIndex].filePath.replace(oldPath, newPath);
        }
        TagService.serializeTagFile(tagFiles)
    }

    public static deleteFilePath(oldPath: string) {
        const tagFiles = TagService.getTagFiles();
        const findIndex = tagFiles.findIndex(value => value.filePath === oldPath);
        if (findIndex !== -1) {
            tagFiles.splice(findIndex, 1)
            TagService.serializeTagFile(tagFiles)
        }
    }

    public static copyFilePath(oldPath: string, newPath: string) {
        const tagFiles = TagService.getTagFiles();
        const findIndex = tagFiles.findIndex(value => value.filePath === oldPath);
        if (findIndex !== -1) {
            tagFiles.push(new TagFileEntity(tagFiles[findIndex].tagIds, newPath))
            TagService.serializeTagFile(tagFiles)
        }
    }

    private static getTags() {
        if (fs.existsSync(TagsPath)) {
            return <TagEntity[]>JSON.parse(fs.readFileSync(TagsPath, 'utf-8'));
        } else {
            return [];
        }
    }

    fillTagIds(file: FileEntity) {
        const tagFiles = TagService.getTagFiles();
        const tagFileEntity = tagFiles.find(value => value.filePath === file.filePath);
        if (tagFileEntity) {
            file.tagIds = tagFileEntity.tagIds
        }
    }

    async save(data: TagEntity) {
        const tagEntities = await this.fetch(null);
        this.uniqueValidate.validate(tagEntities, data)
        if (data.id) {
            const findIndex = tagEntities.findIndex(value => value.id === data.id);
            tagEntities[findIndex] = data
        } else {
            data = TagEntity.of(data)
            tagEntities.push(data)
        }
        TagService.serializeTag(tagEntities);
        return data
    }

    private static serializeTag(tagEntities: Array<TagEntity>) {
        fs.writeFileSync(TagsPath, JSON.stringify(tagEntities))
    }

    async fetchTagFiles(tagIds: string[]): Promise<TagFileEntity[]> {
        let json = TagService.getTagFiles();
        if (tagIds && tagIds.length > 0) {
            json = json.filter(tagFile => tagIds.every(tagId => tagFile.tagIds.includes(tagId)));
        }
        // this.fillTagsInfo(json);
        TagService.fillFileInfo(json)
        return json;
    }

    // private fillTagsInfo(json: TagFileEntity[]) {
    //     const tags = TagService.getTags();
    //     json.forEach(tagFile => {
    //         tagFile.tags = tags.filter(tag => tagFile.tagIds.includes(tag.id))
    //     })
    // }

    private static getTagFiles() {
        if (fs.existsSync(TagFilesPath)) {
            return <TagFileEntity[]>JSON.parse(fs.readFileSync(TagFilesPath, 'utf-8'));
        } else {
            return []
        }
    }

    async saveTagFile(data: TagFileEntity) {
        const that = this
        const tags = TagService.getTags();

        async function tagProcess(idOrValue: string) {
            const index = tags.findIndex(value => value.id === idOrValue);
            let tagEntity: TagEntity;
            if (index === -1) {
                tagEntity = await that.save(new TagEntity(null, idOrValue));
            } else {
                tagEntity = tags[index]
            }
            return tagEntity.id;
        }

        let tagIds = data.tagIds;
        for (let i = 0; i < tagIds.length; i++) {
            tagIds[i] = await tagProcess(tagIds[i])
        }
        const entities = TagService.getTagFiles();
        const findIndex = entities.findIndex(value => value.filePath === data.filePath);
        if (findIndex !== -1) {
            entities[findIndex] = data
        } else {
            entities.push(data)
        }
        TagService.serializeTagFile(entities);
    }

    private static serializeTagFile(entities: TagFileEntity[]) {
        fs.writeFileSync(TagFilesPath, JSON.stringify(entities))
    }

    getByFilePath(filePath: string) {
        let tagFileEntity = TagService.getTagFiles().find(value => value.filePath === filePath);
        if (!tagFileEntity) {
            tagFileEntity = new TagFileEntity([], filePath)
        }
        return tagFileEntity;
    }

    private static fillFileInfo(json: TagFileEntity[]) {
        for (let tagFileEntity of json) {
            tagFileEntity.file = FileService.ofFileEntity(tagFileEntity.filePath);
        }
    }

    remove(ids: string[]) {
        let tags = TagService.getTags();
        tags = tags.filter(value => !ids.includes(value.id));
        const tagFileEntities = TagService.removeTagIdsInTagFile(ids);
        TagService.serializeTag(tags);
        TagService.serializeTagFile(tagFileEntities)
    }

    private static removeTagIdsInTagFile(ids: string[]) {
        let tagFiles = TagService.getTagFiles();
        tagFiles.forEach(value => {
            value.tagIds = value.tagIds.filter(tagId => !ids.includes(tagId))
        })
        tagFiles = tagFiles.filter(value => value.tagIds.length !== 0);
        return tagFiles
    }
}

export class UniqueValidate {
    key;
    idKey = 'id'

    constructor(key) {
        this.key = key;
    }

    validate(data = [], item) {
        const isExist = data.some(value => value[this.key] === item[this.key] && value[this.idKey] !== item[this.idKey]);
        if (isExist) {
            throw new ValidateException(`${this.key}是唯一的,${item[this.key]}已存在!`)
        }
    }
}